//--------------------------------------------------------------------------
// 
//  Copyright (c) Chili Software.  All rights reserved. 
// 
//  File: ObjectView_1.cs
//
//  Description: In memory view on an ObjectSet.
// 
//--------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Collections;
using System.Reflection;

namespace Chili.Opf3
{
	/// <summary>
	/// Represents a databindable, customized view of an <see cref="Opf3.ObjectSet{T}">ObjectSet</see> for sorting, filtering, 
	/// searching, editing, and navigation.
	/// </summary>
	/// <remarks>
	/// <para>
	/// The ObjectView represents a databindable and customized view of an <see cref="Opf3.ObjectSet{T}">ObjectSet</see>. 
	/// The class allows sorting, filtering, searching, editing and navigation in/of the data of the associated 
	/// <see cref="Opf3.ObjectSet{T}">ObjectSet</see>. 
	/// </para>
	/// <para>
	/// The class' content may be customized by specifiying a sort or a filter expression. This class may be also bound to 
	/// a DataGrid, BindingSource or other data-driven controls. The <see cref="Opf3.ObjectSet{T}">ObjectSet</see> returns an 
	/// instance of this class when querying the <see cref="Opf3.ObjectSet{T}.DefaultView">DefaultView</see> property.
	/// </para>
	/// <para>
	/// When sorting or filtering the ObjectView the data of the associated <see cref="Opf3.ObjectSet{T}">ObjectSet</see> is
	/// not affected at all. Each <see cref="Opf3.ObjectSet{T}">ObjectSet</see> may also be associated with more then one 
	/// ObjectView. All ObjectViews are then updated instantely when updating the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.
	/// </para>
	/// </remarks>
	/// <example>
	/// The following example shows how to use the ObjectView in an application.
	///		<code lang="CS">
	/// [Persistent("USER")]
	/// public class User
	/// {
	///		private int _id;
	///		private string _name;
	/// 
	///		[Field("ID", AutoNumber = true, AllowDBNull = false, Identifier = true)]
	///		public int ID
	///		{
	///			get { return _id; }
	///			set { _id = value; }
	///		}
	///
	///		[Field("NAME")]
	///		public string Name
	///		{
	///			get { return _name; }
	///			set { _name = value; }
	///		}
	/// 
	///		// ... Other properties and fields.
	/// }
	/// 
	/// // ... Other code
	/// 
	/// // Loads an ObjectSet of User from the storage and sorts the result descending.
	/// ObjectSet&lt;User&gt; objectSet = context.GetObjectSet&lt;User&gt;();
	/// 
	/// // Create a new BindingSource.
	/// BindingSource bindingSource = new BindingSource();
	/// // Associate the ObjectSet with the DataSource. The ObjectView of the ObjectSet's DefaultView 
	/// // property is used when binding an ObjectSet as data source.
	/// bindingSource.DataSource = objectSet;
	/// // Add a sort string to sort the ObjectView.
	/// bindingSource.Sort = "[ID] ASC, [NAME] DESC";
	/// // Bind the BindingSource to a visual DataGridView.
	/// dataGridView1.DataSource = bindingSource;
	/// 
	/// // Create a new instance of the DataView and set the current ObjectSet as
	/// // associated ObjectSet.
	/// DataView dataView = new DataView(objectSet);
	/// // Sort by the name property in ascending order.
	/// dataView.Sort = "[NAME]";
	/// 
	/// // Loop over all items in the DataView. The items are sorted by the name property.
	/// foreach(User user in dataView)
	/// {
	///		// Do something with the persistent.
	/// }
	///		</code>	
	/// </example>
	public sealed class ObjectView<T> : IList, IList<T>, IBindingList, IRaiseItemChangedEvents, ICancelAddNew, IBindingListView
	{
		#region PropertyComparer

		/// <summary>
		/// This class compares properties of items of the list for sorting purpose.
		/// </summary>
		private sealed class PropertyComparer : IComparer<T>
		{
			private ListSortDescriptionCollection _listSortCollection;

			/// <summary>
			/// Creates a new instance of the <see cref="PropertyComparer">PropertyComparer Class</see>.
			/// </summary>
			/// <param name="listSortCollection">The collection containing the sorting properties and directions.</param>
			public PropertyComparer(ListSortDescriptionCollection listSortCollection)
			{
				if (listSortCollection == null)
					throw new ArgumentNullException("listSortCollection");

				_listSortCollection = listSortCollection;
			}

			#region IComparer<T> Members

			/// <summary>
			/// Compares two objects.
			/// </summary>
			/// <param name="x">First object to compare.</param>
			/// <param name="y">Second object to compare.</param>
			/// <returns></returns>
			public int Compare(T x, T y)
			{
				return Compare(x, y, 0);
			}

			#endregion

			/// <summary>
			/// Compares two objects.
			/// </summary>
			/// <param name="x">First object to compare.</param>
			/// <param name="y">Second object to compare.</param>
			/// <param name="iteration">Specifies the iteration of the comparison.</param>
			public int Compare(T x, T y, int iteration)
			{
				// Leave if no property has left.
				if (_listSortCollection.Count == iteration)
					return 0;

				// Get the property of the current iteration.
				PropertyDescriptor property = _listSortCollection[iteration].PropertyDescriptor;

				// Compare the values of the two properties.
				int result;
				object xValue = property.GetValue(x);
				object yValue = property.GetValue(y);

				if (xValue == null && yValue == null)
					result = 0;
				else if (xValue == null && yValue != null)
					result = -1;
				else if (xValue != null && yValue == null)
					result = 1;
				else
				{
					// Try to convert the xValue to an IComparable.
					IComparable comparable = xValue as IComparable;

					if (comparable != null)
						result = comparable.CompareTo(yValue);
					else
					{
						// If not possible to convert to IComparable convert the item to a string.
						result = xValue.ToString().CompareTo(yValue);
					}
				}

				if (result == 0)
				{
					// We got an equal. Compare the next property.
					return Compare(x, y, iteration + 1);
				}
				else
				{
					// The value is != 0. Apply the direction and return.
					return result * (_listSortCollection[iteration].SortDirection == ListSortDirection.Ascending ? 1 : -1);
				}
			}
		}

		#endregion

		private ObjectSet<T> _objectSet;
		private List<T> _innerList; // Contains the data of the ObjectSet in it's original order and without filter.
		private List<T> _list;
		private int _addNewPos = -1;
		private bool _sorted = false;
		private string _sort = string.Empty;
		private ListSortDescriptionCollection _listSortDescription;

		#region Constructor

		/// <summary>
		/// Creates a new instance of the <see cref="ObjectView{T}">ObjectView Class</see>.
		/// </summary>
		/// <param name="objectSet">The <see cref="Opf3.ObjectSet{T}">ObjectSet</see> bound to the current instance.</param>
		public ObjectView(ObjectSet<T> objectSet)
		{
			this.ObjectSet = objectSet;
		}

		#endregion

		#region Initializing Methods + Main Event Handler

		/// <summary>
		/// Creates the data for the <see cref="ObjectView{T}">ObjectView</see> from the 
		/// data of the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		/// <param name="objectSet">The <see cref="Opf3.ObjectSet{T}">ObjectSet</see> used to build the view.</param>
		private void CreateObjectViewData(ObjectSet<T> objectSet)
		{
			// Copy the data from the ObjectSet to the ObjectView. 
			// This is only a reference copy. Should be very fast!
			_innerList = new List<T>(objectSet);
			_list = new List<T>(objectSet);
		}

		/// <summary>
		/// Invoked each time the list of the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> changed.
		/// </summary>
		/// <param name="sender">The <see cref="Opf3.ObjectSet{T}">ObjectSet</see> itself.</param>
		/// <param name="e">The EventArgs class containing additional information for the event.</param>
		internal void NotifyListChanged(object sender, ListChangedEventArgs e)
		{
			// Call end new.
			((ICancelAddNew)this).EndNew(_addNewPos);

			switch (e.ListChangedType)
			{
				case ListChangedType.ItemAdded:
					ItemAdded(e);
					break;
				case ListChangedType.ItemMoved:
					ItemMoved(e);
					break;
				case ListChangedType.ItemDeleted:
					ItemDeleted(e);
					break;
				case ListChangedType.Reset:
					Reset();
					break;
				case ListChangedType.ItemChanged:
					if (!ItemChanged(e))
						return;
					break;
				default:
					throw new NotSupportedException("Not supported! Please, add the case to the ObjectView<T>.");
			}

			// Fire the event again.
			OnListChanged(e);
		}

		/// <summary>
		/// Invoked each time an item has been changed.
		/// </summary>
		private bool ItemChanged(ListChangedEventArgs e)
		{
			T item = _innerList[e.NewIndex];
			// Return if the item is in the current ObjectView. If not the item needs not to be updated.
			return _list.Contains(item);
		}

		/// <summary>
		/// Invoked each time an item has been deleted.
		/// </summary>
		/// <param name="e">The EventArgs of the event that invoked this method.</param>
		private void ItemDeleted(ListChangedEventArgs e)
		{
			// An item in the ObjectSet has been deleted.
			T item = _innerList[e.NewIndex];
			_innerList.RemoveAt(e.NewIndex);
			_list.Remove(item);
		}

		/// <summary>
		/// Invoked each time an item has been moved.
		/// </summary>
		/// <param name="e">The EventArgs of the event that invoked this method.</param>
		private void ItemMoved(ListChangedEventArgs e)
		{
			// An item in the ObjectSet has been changed.
			T item = _innerList[e.NewIndex];
			_innerList.RemoveAt(e.NewIndex);
			_innerList.Insert(e.NewIndex, this.ObjectSet[e.NewIndex]);
			
			// Get the position of the item in the sorted list.
			int pos = _list.IndexOf(item);
			if (pos == -1)
				return;

			// Remove the current object from that position and add the new one.
			_list.RemoveAt(pos);
			_list.Insert(pos, item);
		}

		/// <summary>
		/// Invoked each time an item has been added.
		/// </summary>
		/// <param name="e">The EventArgs of the event that invoked this method.</param>
		private void ItemAdded(ListChangedEventArgs e)
		{
			T item = _objectSet[e.NewIndex];

			// An item has been added to the ObjectSet.
			_list.Add(item);
			_innerList.Add(item);
		}

		#endregion

		#region Find Methods

		/// <summary>
		/// Determines whether the <see cref="ObjectView{T}">ObjectView</see> contains elements that 
		/// match the conditions defined by the specified predicate. 
		/// </summary>
		/// <param name="match">The Predicate delegate that defines the conditions of the elements to search for.</param>
		/// <returns>true, if the <see cref="ObjectView{T}">ObjectView</see> contains one or more 
		/// elements that match the conditions defined by the specified predicate; otherwise, false.</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		public bool Exists(Predicate<T> match)
		{
			return _list.Exists(match);
		}

		/// <summary>
		/// Searches for an element that matches the conditions defined by the specified predicate, 
		/// and returns the first occurrence within the entire <see cref="ObjectView{T}">ObjectView</see>. 
		/// </summary>
		/// <param name="match">The Predicate delegate that defines the conditions of the element to search for.</param>
		/// <returns>The first element that matches the conditions defined by the specified predicate, if found; 
		/// otherwise, the default value for type T.</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		public T Find(Predicate<T> match)
		{
			return _list.Find(match);
		}

		/// <summary>
		/// Retrieves the all the elements that match the conditions defined by the specified predicate.
		/// </summary>
		/// <param name="match">The Predicate delegate that defines the conditions of the elements to search for.</param>
		/// <returns>An <see cref="ObjectView{T}">ObjectView</see> containing all the elements that match the 
		/// conditions defined by the specified predicate, if found; otherwise, an empty <see cref="ObjectView{T}">ObjectView</see>.</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		public ObjectView<T> FindAll(Predicate<T> match)
		{
			return new ObjectView<T>(new ObjectSet<T>(_list.FindAll(match)));
		}

		/// <summary>
		/// Searches for an element that matches the conditions defined by the specified predicate, and 
		/// returns the zero-based index of the first occurrence within the entire <see cref="ObjectView{T}">ObjectView</see>. 
		/// </summary>
		/// <param name="match">The Predicate delegate that defines the conditions of the element to search for.</param>
		/// <returns>The zero-based index of the first occurrence of an element that 
		/// matches the conditions defined by match, if found; otherwise, -1</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		public int FindIndex(Predicate<T> match)
		{
			return _list.FindIndex(match);
		}

		/// <summary>
		/// Searches for an element that matches the conditions defined by the specified predicate, 
		/// and returns the zero-based index of the first occurrence within the range of elements 
		/// in the <see cref="ObjectView{T}">ObjectView</see> that extends from the specified index 
		/// to the last element. 
		/// </summary>
		/// <param name="startIndex">The zero-based starting index of the search.</param>
		/// <param name="match">The Predicate delegate that defines the conditions of the 
		/// element to search for.</param>
		/// <returns>The zero-based index of the first occurrence of an element that matches the conditions 
		/// defined by match, if found; otherwise, -1.</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		/// <exception cref="System.ArgumentOutOfRangeException">StartIndex is outside the range of valid indexes
		/// for the <see cref="ObjectView{T}">ObjectView</see>.</exception>
		public int FindIndex(int startIndex, Predicate<T> match)
		{
			return _list.FindIndex(startIndex, match);
		}

		/// <summary>
		/// Searches for an element that matches the conditions defined by the specified predicate, 
		/// and returns the zero-based index of the first occurrence within the range of elements 
		/// in the <see cref="ObjectView{T}">ObjectView</see> that starts at the specified index and contains 
		/// the specified number of elements. 
		/// </summary>
		/// <param name="startIndex">The zero-based starting index of the search.</param>
		/// <param name="count">The number of elements in the section to search.</param>
		/// <param name="match">The Predicate delegate that defines the conditions of the 
		/// element to search for.</param>
		/// <returns>The zero-based index of the first occurrence of an element that matches the conditions 
		/// defined by match, if found; otherwise, -1.</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		/// <exception cref="System.ArgumentOutOfRangeException">StartIndex is outside the range of valid indexes
		/// for the <see cref="ObjectView{T}">ObjectView</see> or Count is less then zero.</exception>
		public int FindIndex(int startIndex, int count, Predicate<T> match)
		{
			return _list.FindIndex(startIndex, count, match);
		}

		/// <summary>
		/// Searches for an element that matches the conditions defined by the specified predicate, 
		/// and returns the last occurrence within the entire <see cref="ObjectView{T}">ObjectView</see>.
		/// </summary>
		/// <param name="match">The Predicate delegate that defines the conditions of the element to search for.</param>
		/// <returns>The last element that matches the conditions defined by the specified predicate, if found; 
		/// otherwise, the default value for type T.</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		public T FindLast(Predicate<T> match)
		{
			return _list.FindLast(match);
		}

		/// <summary>
		/// Searches for an element that matches the conditions defined by the specified predicate, 
		/// and returns the zero-based index of the last occurrence within the entire 
		/// <see cref="ObjectView{T}">ObjectView</see>.
		/// </summary>
		/// <param name="match">The Predicate delegate that defines the conditions of the element to search for.</param>
		/// <returns>The zero-based index of the last occurrence of an element that matches the conditions 
		/// defined by match, if found; otherwise, -1.</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		public int FindLastIndex(Predicate<T> match)
		{
			return _list.FindLastIndex(match);
		}

		/// <summary>
		/// Searches for an element that matches the conditions defined by the specified predicate, 
		/// and returns the zero-based index of the last occurrence within the range of elements in 
		/// the <see cref="ObjectView{T}">ObjectView</see> that extends from the first element 
		/// to the specified index.
		/// </summary>
		/// <param name="startIndex">The zero-based starting index of the backward search.</param>
		/// <param name="match">The Predicate delegate that defines the conditions of the element to search for.</param>
		/// <returns>The zero-based index of the last occurrence of an element that matches the conditions 
		/// defined by match, if found; otherwise, -1.</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		/// <exception cref="System.ArgumentOutOfRangeException">StartIndex is outside the range of valid
		/// indexes for the <see cref="ObjectView{T}">ObjectView</see>.</exception>
		public int FindLastIndex(int startIndex, Predicate<T> match)
		{
			return _list.FindLastIndex(startIndex, match);
		}

		/// <summary>
		/// Searches for an element that matches the conditions defined by the specified predicate, 
		/// and returns the zero-based index of the last occurrence within the range of elements 
		/// in the <see cref="ObjectView{T}">ObjectView</see> that contains the specified number of elements 
		/// and ends at the specified index. 
		/// </summary>
		/// <param name="startIndex">The zero-based starting index of the backward search.</param>
		/// <param name="count">The number of elements in the section to search.</param>
		/// <param name="match">The Predicate delegate that defines the conditions of the element to search for.</param>
		/// <returns>The zero-based index of the last occurrence of an element that matches the conditions 
		/// defined by match, if found; otherwise, -1. </returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		/// <exception cref="System.ArgumentOutOfRangeException">StartIndex is outside the range of valid indexes
		/// for the <see cref="ObjectView{T}">ObjectView</see> or Count is less then zero.</exception>
		public int FindLastIndex(int startIndex, int count, Predicate<T> match)
		{
			return _list.FindLastIndex(startIndex, count, match);
		}

		#endregion

		/// <summary>
		/// Specifies the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> connected with the <see cref="ObjectView{T}">ObjectView</see>.
		/// </summary>
		/// <exception cref="ArgumentNullException">Value is null.</exception>
		public ObjectSet<T> ObjectSet
		{
			get { return _objectSet; }
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");
				Initialize(value);
			}
		}

		/// <summary>
		/// Initializes the <see cref="ObjectView{T}">ObjectView</see>.
		/// </summary>
		/// <param name="value">The ObjectSet that is bound to the <see cref="ObjectView{T}">ObjectView</see>.</param>
		private void Initialize(ObjectSet<T> value)
		{
			// Unregisters the view in the manager of the current ObjectSet.
			UnRegisterViewInManager();
			// Set the new ObjectSet as value.
			_objectSet = value;
			// Register the view in the ObjectViewManager.
			_objectSet.ObjectViewManager.RegisterView(this);

			// Reset the ObjectView.
			Reset();
		}

		/// <summary>
		/// Unregisters the <see cref="ObjectView{T}">ObjectView</see> in the <see cref="Opf3.ObjectViewManager{T}">ObjectViewManager</see>
		/// of the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		private void UnRegisterViewInManager()
		{
			if (_objectSet != null)
			{
				// Unregister this view in the manager.
				_objectSet.ObjectViewManager.UnRegisterView(this);
			}
		}

		/// <summary>
		/// Resets the <see cref="ObjectView{T}">ObjectView</see>.
		/// </summary>
		public void Reset()
		{
			// Removes the sort and the filter of the ObjectView.
			((IBindingList)this).RemoveSort();
			((IBindingListView)this).RemoveFilter();

			// Re-Creates the data for the ObjectView from the ObjectSet.
			CreateObjectViewData(_objectSet);
		}

		/// <summary>
		/// Creates and returns a new <see cref="Opf3.ObjectSet{T}">ObjectSet</see> based on the objects in an existing <see cref="ObjectView{T}">ObjectView</see>.
		/// </summary>
		/// <returns>A new <see cref="ObjectView{T}">ObjectView</see> instance that contains the requested objects.</returns>
		public ObjectSet<T> ToObjectSet()
		{
			return new ObjectSet<T>(_list);
		}

		/// <summary>
		/// Gets the object at the specified index.
		/// </summary>
		/// <param name="index">The zero-based index of the object to get or set.</param>
		/// <returns>The object at the specified index.</returns>
		/// <exception cref="System.ArgumentOutOfRangeException">Index is less then zero or index is equal or greater
		/// then <see cref="Opf3.ObjectView{T}.Count">Count</see>.</exception>
		public T this[int index]
		{
			get
			{
				return ((IList<T>)this)[index];
			}
		}

		#region Overriden Members

		/// <summary>
		/// Returns the string representation for the object.
		/// </summary>
		public override string ToString()
		{
			return string.Format("{0}, Count = {1}", base.ToString(), this.Count);
		}

		#endregion

		#region IList<T> Members

		/// <summary>
		/// Determines the index of a specific item in the <see cref="Opf3.ObjectView{T}">ObjectView</see>.
		/// </summary>
		/// <param name="item">The object to locate in the <see cref="Opf3.ObjectView{T}">ObjectView</see>.</param>
		/// <returns>The index of item if found in the list; otherwise, -1.</returns>
		public int IndexOf(T item)
		{
			return _list.IndexOf(item);
		}

		/// <summary>
		/// Inserts an item to the <see cref="Opf3.ObjectView{T}">ObjectView</see> at the specified index. 
		/// </summary>
		/// <param name="index">The zero-based index at which item should be inserted.</param>
		/// <param name="item">The object to insert into the <see cref="Opf3.ObjectView{T}">ObjectView</see>.</param>
		void IList<T>.Insert(int index, T item)
		{
			throw new ArgumentException("Use the external ObjectSet to insert an object.");
		}

		/// <summary>
		/// Moves the object at the specified index from the <see cref="Opf3.ObjectView{T}">ObjectView</see> to
		/// to the connected <see cref="Opf3.ObjectSet{T}.RemovedList">ObjectSet's</see> <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see>.
		/// All objects on the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see> are deleted 
		/// when the changes on the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> are persisted.
		/// </summary>
		/// <param name="index">The zero-based index of the item to move.</param>
		/// <exception cref="System.ArgumentOutOfRangeException">Index is less than zero or index is equal to
		/// or greater than <see cref="Opf3.ObjectView{T}.Count">Count</see>.</exception>
		public void RemoveAt(int index)
		{
			// Get the item at the given index.
			T item = _list[index];
			this.ObjectSet.Remove(item);
		}

		/// <summary>
		/// Gets the object at the specified index.
		/// </summary>
		/// <param name="index">The zero-based index of the object to get or set.</param>
		/// <returns>The object at the specified index.</returns>
		/// <exception cref="System.ArgumentOutOfRangeException">Index is less then zero or index is equal or greater
		/// then <see cref="Opf3.ObjectView{T}.Count">Count</see>.</exception>
		T IList<T>.this[int index]
		{
			get
			{
				return _list[index];
			}
			set
			{
				throw new ArgumentException("Use the external ObjectSet to modify an object.");
			}
		}

		#endregion

		#region ICollection<T> Members

		/// <summary>
		/// Adds an item to the <see cref="Opf3.ObjectView{T}">ObjectView</see>.
		/// </summary>
		/// <param name="item">The object to add to the <see cref="Opf3.ObjectView{T}">ObjectView</see>.</param>
		void ICollection<T>.Add(T item)
		{
			if (item == null)
			{
				((IBindingList)this).AddNew();
			}

			throw new ArgumentException("Use the external ObjectSet to add new objects to the list. Only AddNew is supported here.");
		}

		/// <summary>
		/// Removes all items from the <see cref="Opf3.ObjectView{T}">ObjectView</see>.
		/// </summary>
		void ICollection<T>.Clear()
		{
			throw new ArgumentException("Use the external ObjectSet to clear the objects.");
		}

		/// <summary>
		/// Determines whether the <see cref="Opf3.ObjectView{T}">ObjectView</see> contains a specific object.
		/// </summary>
		/// <param name="item">The object to located in the <see cref="Opf3.ObjectView{T}">ObjectView</see>.</param>
		/// <returns>True, if item is found in the <see cref="Opf3.ObjectView{T}">ObjectView</see>; otherwise, false.</returns>
		public bool Contains(T item)
		{
			return _list.Contains(item);
		}

		/// <summary>
		/// Copies the elements of the <see cref="Opf3.ObjectView{T}">ObjectView</see> to an Array, starting at 
		/// a particular Array index.
		/// </summary>
		/// <param name="array">The one-dimensional Array that is the destination of the elements copied 
		/// from <see cref="Opf3.ObjectView{T}">ObjectView</see>.</param>
		/// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
		/// <exception cref="System.ArgumentNullException">Array is null.</exception>
		/// <exception cref="System.ArgumentException">Array is multidimensional, array does not have zero-based
		/// indexing, the number of elements in the source is greater than the number of elements that the 
		/// destination array can contain or type T cannot be cast automatically to the type of the destination
		/// array.</exception>
		public void CopyTo(T[] array, int arrayIndex)
		{
			_list.CopyTo(array, arrayIndex);
		}

		/// <summary>
		/// Gets the number of elements contained in the <see cref="Opf3.ObjectView{T}">ObjectView</see>. 
		/// </summary>
		/// <value>The number of elements contained in the <see cref="Opf3.ObjectView{T}">ObjectView</see>.</value>
		public int Count
		{
			get { return _list.Count; }
		}

		/// <summary>
		/// Gets a value indicating whether the <see cref="Opf3.ObjectView{T}">ObjectView</see> is read-only.
		/// </summary>
		/// <value>True, if the <see cref="Opf3.ObjectView{T}">ObjectView</see> is read-only; otherwise, false.</value>
		bool ICollection<T>.IsReadOnly
		{
			get 
			{
				if (this.ObjectSet != null)
					return this.ObjectSet.IsReadOnly;
				return false;
			}
		}

		/// <summary>
		/// Removes the first occurrence of a specific object from the <see cref="Opf3.ObjectView{T}">ObjectView</see>
		/// to the connected <see cref="Opf3.ObjectSet{T}.RemovedList">ObjectSet's</see> <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see>.
		/// All objects on the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see> are deleted 
		/// when the changes on the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> are persisted.
		/// </summary>
		/// <param name="item">The object to remove from the <see cref="Opf3.ObjectView{T}">ObjectView</see>.</param>
		/// <returns>True, if item was successfully removed from the <see cref="Opf3.ObjectView{T}">ObjectView</see>; otherwise, false. 
		/// This method also returns false if item was not found in the original 
		/// <see cref="Opf3.ObjectView{T}">ObjectView</see>.</returns>
		public bool Remove(T item)
		{
			int index = this.IndexOf(item);
			if (index == -1)
				return false;

			// Remove the item.
			this.ObjectSet.Remove(item);
			return true;
		}

		#endregion

		#region IEnumerable<T> Members

		/// <summary>
		/// Returns an enumerator that iterates through this <see cref="Opf3.ObjectView{T}">ObjectView</see>.
		/// </summary>
		public IEnumerator<T> GetEnumerator()
		{
			return _list.GetEnumerator();
		}

		#endregion

		#region IList Members

		/// <summary>
		/// Adds an item to the <see cref="Opf3.ObjectView{T}">ObjectView</see>.
		/// </summary>
		/// <param name="value">The object to add to the <see cref="Opf3.ObjectView{T}">ObjectView</see>.</param>
		int IList.Add(object value)
		{
			((ICollection<T>)this).Add((T)value);
			return this.Count - 1;
		}

		/// <summary>
		/// Removes all items from the <see cref="Opf3.ObjectView{T}">ObjectView</see>.
		/// </summary>
		void IList.Clear()
		{
			((ICollection<T>)this).Clear();
		}

		/// <summary>
		/// Determines whether the <see cref="Opf3.ObjectView{T}">ObjectView</see> contains a specific object.
		/// </summary>
		/// <param name="value">The object to located in the <see cref="Opf3.ObjectView{T}">ObjectView</see>.</param>
		/// <returns>True, if item is found in the <see cref="Opf3.ObjectView{T}">ObjectView</see>; otherwise, false.</returns>
		bool IList.Contains(object value)
		{
			return this.Contains((T)value);
		}

		/// <summary>
		/// Determines the index of a specific item in the <see cref="Opf3.ObjectView{T}">ObjectView</see>.
		/// </summary>
		/// <param name="value">The object to locate in the <see cref="Opf3.ObjectView{T}">ObjectView</see>.</param>
		/// <returns>The index of item if found in the list; otherwise, -1.</returns>
		int IList.IndexOf(object value)
		{
			return _list.IndexOf((T)value);
		}

		/// <summary>
		/// Inserts an item to the <see cref="Opf3.ObjectView{T}">ObjectView</see> at the specified index. 
		/// </summary>
		/// <param name="index">The zero-based index at which item should be inserted.</param>
		/// <param name="value">The object to insert into the <see cref="Opf3.ObjectView{T}">ObjectView</see>.</param>
		void IList.Insert(int index, object value)
		{
			((IList<T>)this).Insert(index, (T)value);
		}

		/// <summary>
		/// Gets a value indicating whether the <see cref="ObjectView{T}">ObjectView</see> has a fixed size.
		/// </summary>
		/// <returns>True if the <see cref="ObjectView{T}">ObjectView</see> has a fixed size; otherwise, false.</returns>
		bool IList.IsFixedSize
		{
			get 
			{
				if (this.ObjectSet != null)
					return ((IList)this.ObjectSet).IsFixedSize;
				return false; 
			}
		}

		/// <summary>
		/// Gets a value indicating whether the <see cref="Opf3.ObjectView{T}">ObjectView</see> is read-only.
		/// </summary>
		/// <value>True, if the <see cref="Opf3.ObjectView{T}">ObjectView</see> is read-only; otherwise, false.</value>
		bool IList.IsReadOnly
		{
			get 
			{
				return ((ICollection<T>)this).IsReadOnly;
			}
		}

		/// <summary>
		/// Removes the first occurrence of a specific object from the <see cref="Opf3.ObjectView{T}">ObjectView</see>
		/// to the connected <see cref="Opf3.ObjectSet{T}.RemovedList">ObjectSet's</see> <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see>.
		/// All objects on the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see> are deleted 
		/// when the changes on the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> are persisted.
		/// </summary>
		/// <param name="value">The object to remove from the <see cref="Opf3.ObjectView{T}">ObjectView</see>.</param>
		void IList.Remove(object value)
		{
			this.Remove((T)value);
		}

		/// <summary>
		/// Moves the object at the specified index from the <see cref="Opf3.ObjectView{T}">ObjectView</see> to
		/// to the connected <see cref="Opf3.ObjectSet{T}.RemovedList">ObjectSet's</see> <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see>.
		/// All objects on the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see> are deleted 
		/// when the changes on the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> are persisted.
		/// </summary>
		/// <param name="index">The zero-based index of the item to move.</param>
		/// <exception cref="System.ArgumentOutOfRangeException">Index is less than zero or index is equal to
		/// or greater than <see cref="Opf3.ObjectView{T}.Count">Count</see>.</exception>
		void IList.RemoveAt(int index)
		{
			this.RemoveAt(index);

			// Reset the list... Seems like this needs to be done after a delete from the DataGridView.
			OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, index));
		}

		/// <summary>
		/// Gets the object at the specified index.
		/// </summary>
		/// <param name="index">The zero-based index of the object to get or set.</param>
		/// <returns>The object at the specified index.</returns>
		/// <exception cref="System.ArgumentOutOfRangeException">Index is less then zero or index is equal or greater
		/// then <see cref="Opf3.ObjectView{T}.Count">Count</see>.</exception>
		object IList.this[int index]
		{
			get
			{
				return ((IList<T>)this)[index];
			}
			set
			{
				((IList<T>)this)[index] = (T)value;
			}
		}

		#endregion

		#region ICollection Members

		/// <summary>
		/// Copies the elements of the <see cref="Opf3.ObjectView{T}">ObjectView</see> to an Array, starting at 
		/// a particular Array index.
		/// </summary>
		/// <param name="array">The one-dimensional Array that is the destination of the elements copied 
		/// from <see cref="Opf3.ObjectView{T}">ObjectView</see>.</param>
		/// <param name="index">The zero-based index in array at which copying begins.</param>
		/// <exception cref="System.ArgumentNullException">Array is null.</exception>
		/// <exception cref="System.ArgumentException">Array is multidimensional, array does not have zero-based
		/// indexing, the number of elements in the source is greater than the number of elements that the 
		/// destination array can contain or type T cannot be cast automatically to the type of the destination
		/// array.</exception>
		void ICollection.CopyTo(Array array, int index)
		{
			T[] t = new T[array.Length];
			array.CopyTo(t, 0);

			this.CopyTo(t, index);
		}

		/// <summary>
		/// Gets the number of elements contained in the <see cref="Opf3.ObjectView{T}">ObjectView</see>. 
		/// </summary>
		/// <value>The number of elements contained in the <see cref="Opf3.ObjectView{T}">ObjectView</see>.</value>
		int ICollection.Count
		{
			get { return this.Count; }
		}

		/// <summary>
		/// Gets a value indicating whether access to the <see cref="ObjectView{T}">ObjectView</see> is synchronized (thread safe).
		/// </summary>
		bool ICollection.IsSynchronized
		{
			get { return false; }
		}

		/// <summary>
		/// Gets an object that can be used to synchronize access to the <see cref="ObjectView{T}">ObjectView</see>.
		/// </summary>
		object ICollection.SyncRoot
		{
			get { return this; }
		}

		#endregion

		#region IEnumerable Members

		/// <summary>
		/// Returns an enumerator that iterates through this <see cref="Opf3.ObjectView{T}">ObjectView</see>.
		/// </summary>
		IEnumerator IEnumerable.GetEnumerator()
		{
			return (IEnumerator)GetEnumerator();
		}

		#endregion

		#region IBindingList Members

		/// <summary>
		/// Occurs when the <see cref="ObjectView{T}">ObjectView</see> changes.
		/// </summary>
		/// <remarks>
		/// This event is fired each time the <see cref="ObjectView{T}">ObjectView</see> changes to notify 
		/// that the object has been changed. It allows to act after the object has been changed.
		/// </remarks>
		public event ListChangedEventHandler ListChanged;

		/// <summary>
		/// Invokes the ListChanged event.
		/// </summary>
		/// <param name="e"></param>
		private void OnListChanged(ListChangedEventArgs e)
		{
			if (ListChanged != null)
				ListChanged(this, e);
		}

		/// <summary>
		/// Adds a new item to the list.
		/// </summary>
		/// <returns>The item added to the list.</returns>
		/// <exception cref="NotSupportedException">The object to create has no public or private constructor without arguments.</exception>
		public object AddNew()
		{
			T item = default(T);
			try
			{
				item = (T)ObjectPool.Current.GetObject(typeof(T));
			}
			catch (Exception ex)
			{
				// Throw an exception if we are not able to create an instance of the object.
				throw new NotSupportedException("The object must have a public or private constructor without arguments.", ex);
			}
			
			this.ObjectSet.Add(item);
			// Get the index of the new item. The new item is added over the ObjectSet.
			_addNewPos = ((IList)this).IndexOf(item);
			return item;
		}

		/// <summary>
		/// Gets whether you can update items in the list.
		/// </summary>
		public bool AllowEdit
		{
			get { return true; }
		}

		/// <summary>
		/// Gets whether you can add items to the list using <see cref="IBindingList.AddNew">AddNew</see> .
		/// </summary>
		public bool AllowNew
		{
			get { return true; }
		}

		/// <summary>
		/// Gets whether you can remove items from the list, using Remove or RemoveAt.
		/// </summary>
		public bool AllowRemove
		{
			get { return true; }
		}

		/// <summary>
		/// Gets whether the list supports searching using the Find method.
		/// </summary>
		bool IBindingList.SupportsSearching
		{
			get { return true; }
		}

		/// <summary>
		/// Returns the index of the item that has the given PropertyDescriptor.
		/// </summary>
		/// <param name="property">The PropertyDescriptor to search on.</param>
		/// <param name="key">The value of the property parameter to search for.</param>
		/// <returns>The index of the row that has the given PropertyDescriptor.</returns>
		int IBindingList.Find(PropertyDescriptor property, object key)
		{
			// Searches the list for the item with the given value at the property descriptor.
			return _list.FindIndex(delegate(T obj)
			{
				if (obj == null)
					return false;

				// Get the value from the property.
				object value = property.GetValue(obj);
				if (value != null)
				{
					// Validate the value of the property agains the given key.
					return value.Equals(key);
				}
				return false;
			});
		}

		/// <summary>
		/// Gets whether a <see cref="IBindingList.ListChanged">ListChanged</see> event is raised when the list changes or an item in the list changes.
		/// </summary>
		bool IBindingList.SupportsChangeNotification
		{
			get { return true; }
		}

		/// <summary>
		/// Gets whether the list supports sorting.
		/// </summary>
		bool IBindingList.SupportsSorting
		{
			get { return true; }
		}

		/// <summary>
		/// Sorts the list based on a PropertyDescriptor and a ListSortDirection.
		/// </summary>
		/// <param name="property">The PropertyDescriptor to sort by.</param>
		/// <param name="direction">One of the ListSortDirection values.</param>
		void IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction)
		{
			// Set the sort string of the ObjectSet.
			this.Sort = CreateSortString(property, direction);
		}

		/// <summary>
		/// Gets whether the items in the list are sorted.
		/// </summary>
		bool IBindingList.IsSorted
		{
			get { return _sorted; }
		}

		/// <summary>
		/// Removes any sort applied using <see cref="IBindingList.ApplySort">ApplySort</see>.
		/// </summary>
		void IBindingList.RemoveSort()
		{
			_sorted = false;
			_sort = string.Empty;
			_listSortDescription = null;
		}

		/// <summary>
		/// Gets the direction of the sort.
		/// </summary>
		ListSortDirection IBindingList.SortDirection
		{
			get
			{
				if (_listSortDescription != null && _listSortDescription.Count == 1)
					return _listSortDescription[0].SortDirection;

				return ListSortDirection.Ascending;
			}
		}

		/// <summary>
		/// Gets the PropertyDescriptor that is being used for sorting.
		/// </summary>
		PropertyDescriptor IBindingList.SortProperty
		{
			get
			{
				if (_listSortDescription != null && _listSortDescription.Count == 1)
					return _listSortDescription[0].PropertyDescriptor;

				return null;
			}
		}

		/// <summary>
		/// Adds the PropertyDescriptor to the indexes used for searching.
		/// </summary>
		/// <param name="property">The PropertyDescriptor to add to the indexes used for searching.</param>
		void IBindingList.AddIndex(PropertyDescriptor property)
		{
		}

		/// <summary>
		/// Removes the PropertyDescriptor from the indexes used for searching.
		/// </summary>
		/// <param name="property">The PropertyDescriptor to remove from the indexes used for searching.</param>
		void IBindingList.RemoveIndex(PropertyDescriptor property)
		{
		}

		#endregion

		#region IRaiseItemChangedEvents Members

		/// <summary>
		/// Gets a value indicating whether the object raises the <see cref="ListChanged">ListChanged</see> event on changes in the managed items.
		/// </summary>
		bool IRaiseItemChangedEvents.RaisesItemChangedEvents
		{
			get { return ((IRaiseItemChangedEvents)this.ObjectSet).RaisesItemChangedEvents; }
		}

		#endregion

		#region ICancelAddNew Members

		/// <summary>
		///  Discards a pending new item from the collection.
		/// </summary>
		/// <param name="itemIndex">The index of the item that was previously added to the collection.</param>
		void ICancelAddNew.CancelNew(int itemIndex)
		{
			if (_addNewPos >= 0 && _addNewPos == itemIndex)
			{
				T item = _list[_addNewPos];
				// Remove the item from the objectSet and RemovedList.
				this.ObjectSet.Remove(item);
				this.ObjectSet.RemovedList.Remove(item);

				// Reset the position to add a new item.
				_addNewPos = -1;
			}
		}

		/// <summary>
		/// Commits a pending new item to the collection.
		/// </summary>
		/// <param name="itemIndex">The index of the item that was previously added to the collection.</param>
		void ICancelAddNew.EndNew(int itemIndex)
		{
			// REMOVED: Caused problems with added items that have been removed before being compiled.
			//          _addNewPos is now only changed if a new item is added or if CancelNew is called.
			//          Seems not to cause any problems that way.

			//if (_addNewPos >= 0 && _addNewPos == itemIndex)
			//{
			//    _addNewPos = -1;
			//}
		}

		#endregion

		#region Sort Methods

		/// <summary>
		/// Creates the sort string for one property descriptior.
		/// </summary>
		/// <param name="property">The sort string is created for this property.</param>
		/// <param name="direction">The direction of the sort.</param>
		private string CreateSortString(PropertyDescriptor property, ListSortDirection direction)
		{
			StringBuilder builder = new StringBuilder();

			// Add the property.
			builder.Append('[');
			builder.Append(property.Name);
			builder.Append(']');

			// ... and the sort direction.
			if (ListSortDirection.Descending == direction)
				builder.Append(" DESC");

			return builder.ToString();
		}

		/// <summary>
		/// Specifies the string to sort the <see cref="Opf3.ObjectView{T}">ObjectView</see>.
		/// </summary>
		/// <remarks>
		/// This method allows to specify a string that sorts the <see cref="Opf3.ObjectView{T}">ObjectView</see>. The string
		/// contains the property and the direction of the sort. You may also specify more the one property and direction.
		/// </remarks>
		/// <example>
		/// The following examples show how to sort a <see cref="Opf3.ObjectView{T}">ObjectView</see> by different properties in
		/// different directions.
		/// <code lang="CS">
		/// // Load an ObjectSet with data.
		/// ObjectSet&lt;User&gt; objectSet = context.GetObjectSet&lt;User&gt;();
		/// // Get the default view of the ObjectSet.
		/// ObjectView&lt;User&gt; objectView = objectSet.DefaultView;
		/// // Sorts the ObjectView by the ID property (in ascending order) and by the NAME
		/// // property (in the descending order).
		/// objectView.Sort = "[ID] ASC, [NAME] DESC";
		/// </code>
		/// <code lang="CS">
		/// // Load an ObjectSet with data.
		/// ObjectSet&lt;User&gt; objectSet = context.GetObjectSet&lt;User&gt;();
		/// // Create a new ObjectView.
		/// ObjectView&lt;User&gt; objectView = new ObjectView&lt;User&gt;();
		/// // Set the current ObjectSet as source of the view.
		/// objectView.ObjectSet = objectSet;
		/// // Sorts the ObjectView by the ID property (in ascending order).
		/// objectView.Sort = "[ID] ASC";
		/// </code>
		/// </example>
		public string Sort
		{
			get { return _sort; }
			set
			{
				// Create the description list from the string.
				_listSortDescription = ParseSortString(value);
				_sort = value;
				_sorted = true;
				// Sort the list.
				_list.Sort(new PropertyComparer(_listSortDescription));

				// Notify that the list has been changed.
				OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
			}
		}

		/// <summary>
		/// Parses and returns the given sort string.
		/// </summary>
		/// <param name="value">The sort string that is parsed.</param>
		private ListSortDescriptionCollection ParseSortString(string value)
		{
			// Return an empty collection if there is nothing to parse.
			if (value == null || value.Length == 0)
				return new ListSortDescriptionCollection();

			// Get the ReflectPropertyDescriptor type.
			Type type = Assembly.GetAssembly(typeof(PropertyDescriptor)).GetType("System.ComponentModel.ReflectPropertyDescriptor");

			string[] split = value.Split(',');
			ListSortDescription[] descriptions = new ListSortDescription[split.Length];

			for (int i = 0; i < split.Length; i++)
			{
				string item = split[i].Trim();
				int length = item.Length;
				ListSortDirection direction = ListSortDirection.Ascending;

				// Check if the sort item contains a DESC or ASC
				if (length >= 5 && string.Compare(item, (length - 4), " ASC", 0, 4, StringComparison.OrdinalIgnoreCase) == 0)
					item = item.Substring(0, length - 4).Trim();
				else if (length >= 6 && (string.Compare(item, (length - 5), " DESC", 0, 5, StringComparison.OrdinalIgnoreCase) == 0))
				{
					direction = ListSortDirection.Descending;
					item = item.Substring(0, length - 5).Trim();
				}

				// Understand if the property is surrounded by brakets.
				if (item.StartsWith("[", StringComparison.Ordinal))
				{
					if (!item.EndsWith("]", StringComparison.Ordinal))
						throw new ArgumentException(string.Format("If the property name '{0}' starts with a [ it must also end with a ]", item));
					item = item.Substring(1, item.Length - 2);
				}

				// Check if the property of the descriptor is found in the object type.
				PropertyInfo property = typeof(T).GetProperty(item, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, null, new Type[] { }, null);
				if (property == null)
					throw new ArgumentException(string.Format("Property '{0}' not found.", item));

				// Create an instance of the ReflectPropertyDescriptor type.
				object[] attrs = property.GetCustomAttributes(true);
				Attribute[] attributes = new Attribute[attrs.Length];
				Array.Copy(attrs, attributes, attrs.Length);
				PropertyDescriptor descriptor = (PropertyDescriptor)Activator.CreateInstance(type, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null,
					new object[] { typeof(T), item, property.PropertyType, property, property.GetGetMethod(), property.GetSetMethod(), attributes }, null);

				descriptions[i] = new ListSortDescription(descriptor, direction);
			}
			return new ListSortDescriptionCollection(descriptions);
		}

		#endregion

		#region IBindingListView Members

		/// <summary>
		/// Sorts the <see cref="ObjectView{T}">ObjectView</see> based on the given 
		/// <see cref="System.ComponentModel.ListSortDescriptionCollection">ListSortDescriptionCollection</see>.
		/// </summary>
		/// <param name="sorts">The <see cref="System.ComponentModel.ListSortDescriptionCollection">ListSortDescriptionCollection</see> 
		/// containing the sorts to apply to the data source.</param>
		/// <exception cref="ArgumentNullException">Sorts is null.</exception>
		/// <exception cref="ArgumentException">A property to sort by, is not found.</exception>
		void IBindingListView.ApplySort(ListSortDescriptionCollection sorts)
		{
			if (sorts == null)
				throw new ArgumentNullException("sorts");

			StringBuilder builder = new StringBuilder();
			for (int i = 0; i < sorts.Count; i++)
			{
				ListSortDescription description = sorts[i];
				// Skip empty descriptions.
				if (description == null)
					continue;

				PropertyDescriptor descriptor = description.PropertyDescriptor;

				// Check if the property of the descriptor is found in the object type.
				if (typeof(T).GetProperty(descriptor.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) == null)
					throw new ArgumentException(string.Format("Property '{0}' not found.", descriptor.Name));

				if (i > 0)
					builder.Append(',');

				builder.Append(CreateSortString(descriptor, description.SortDirection));
			}

			this.Sort = builder.ToString();
		}

		/// <summary>
		/// Gets whether the list supports advanced sorting.
		/// </summary>
		bool IBindingListView.SupportsAdvancedSorting
		{
			get { return true; }
		}

		/// <summary>
		/// Gets the collection of sort descriptions currently applied to the <see cref="Opf3.ObjectView{T}">ObjectView</see>.
		/// </summary>
		ListSortDescriptionCollection IBindingListView.SortDescriptions
		{
			get { return _listSortDescription; }
		}

		/// <summary>
		/// Gets a value indicating whether the data source supports filtering.
		/// </summary>
		bool IBindingListView.SupportsFiltering
		{
			get { return false; }
		}

		/// <summary>
		/// NOT SUPPORTED YET.
		/// </summary>
		string IBindingListView.Filter
		{
			get
			{
				throw new NotSupportedException();
			}
			set
			{
				throw new NotSupportedException();
			}
		}

		/// <summary>
		/// NOT SUPPORTED YET.
		/// </summary>
		void IBindingListView.RemoveFilter()
		{
		}

		#endregion
	}
}
